﻿using System;
using System.Linq;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Centido.Core.Test
{
    /// <summary>
    ///   Testing different restoring modes.
    /// </summary>
    [TestClass]
    public class RestoringModesTest
    {
        /// <summary>
        ///   Domain name
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        ///   Domain
        /// </summary>
        private Domain _domain;

        /// <summary>
        ///   The table name.
        /// </summary>
        private string _tableName;

        /// <summary>
        ///   The column name.
        /// </summary>
        private string _columnName;
        
        /// <summary>
        ///   The user name.
        /// </summary>
        private string _userName;

        /// <summary>
        ///   The role name.
        /// </summary>
        private string _roleName;

        /// <summary>
        ///   The unit name.
        /// </summary>
        private string _unitName;

        /// <summary>
        ///   The row id.
        /// </summary>
        private Guid _rowId;

        /// <summary>
        ///   The deleted row id.
        /// </summary>
        private Guid _deletedRowId;

        /// <summary>
        ///   The inserted row owner.
        /// </summary>
        private string _rowOwner;

        /// <summary>
        ///   The inserted row version.
        /// </summary>
        private Guid _rowVersion;


        /// <summary>
        ///   Initializing the test
        /// </summary>
        [TestInitialize]
        public void Initialize()
        {
            _domain = Domain.Build(DomainConfiguration.Load(DomainName));

            _domain.DeleteAll();

            // Creating a table
            var table = _domain.AddTable((_tableName = StringProperty.RandomIdentifier()));

            // Creating table columns
            var column = table.AddColumn((_columnName = StringProperty.RandomIdentifier()), ColumnType.Integer);

            // Adding an index
            table.AddIndex(column);

            // Inserting a row
            _rowId = table.InsertRow(DataSource.InternalTable,
                table.SystemColumns.Owner.Value((_rowOwner = "my_owner")));

            // Updating that row
            table.SystemColumns.RowVersion.Update(_rowId, DataSource.InternalTable,
                (_rowVersion = Guid.NewGuid()));

            // Inserting a deleted row
            _deletedRowId = table.InsertRow(DataSource.InternalTable,
                table.SystemColumns.IsActive.Value(false));

            // Creating a user
            var user = _domain.AddUser((_userName = StringProperty.RandomIdentifier()),
                StringProperty.RandomPassword());

            // Creating a role
            var role = _domain.AddRole((_roleName = StringProperty.RandomIdentifier()));

            // Adding the user to the role
            role.AddUser(user);

            // Creating a unit
            var unit = _domain.AddUnit((_unitName = StringProperty.RandomIdentifier()));

            // Adding the user to the unit
            unit.AddUser(user);

            // Granting permissions to the user
            table.GrantAll(user);

            // Granting permissions tp the role
            table.GrantAll(role);
        }


        /// <summary>
        ///   Cleaning up after the test
        /// </summary>
        [TestCleanup]
        public void CleanUp()
        {
            _domain.DeleteAll();
        }


        /// <summary>
        ///   Backuping and restoring the current domain.
        /// </summary>
        /// <param name="components">The backuped components.</param>
        private void BackupAndRestore(DomainComponents components)
        {
            // Creating a backup
            new BackupTask(_domain) { IncludedComponents = components }.Run();

            // Deleting all tables
            _domain.DeleteAllTables();

            Assert.AreEqual(_domain.Tables.Count, 0);

            // Restoring
            new RestoreTask(_domain).Run();
        }


        /// <summary>
        ///   Restoring none at all.
        /// </summary>
        [TestMethod]
        public void RestoringNoneAtAll()
        {
            BackupAndRestore(DomainComponents.None);

            Assert.IsNull(_domain.GetTable(_tableName));
            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetRole(_roleName));
            Assert.IsNull(_domain.GetUnit(_unitName));
        }


        /// <summary>
        ///   Restoring the schema.
        /// </summary>
        [TestMethod]
        public void RestoringSchema()
        {
            BackupAndRestore(DomainComponents.Schema);

            var table = _domain.GetTable(_tableName);

            Assert.IsNotNull(table);
            Assert.IsNotNull(table.GetColumn(_columnName));
            Assert.IsTrue(table.Indexes.Any(i => i.Columns.Any(c => c.Name == _columnName.ToLower())));

            Assert.IsFalse(table.ContainsRow(_rowId, DataSource.InternalTable));

            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetRole(_roleName));
            Assert.IsNull(_domain.GetUnit(_unitName));
        }


        /// <summary>
        ///   Restoring users.
        /// </summary>
        [TestMethod]
        public void RestoringUsers()
        {
            BackupAndRestore(DomainComponents.Users);

            var user = _domain.GetUser(_userName);

            Assert.IsNotNull(user);

            Assert.AreEqual(user.Roles.Count, 0);
            Assert.AreEqual(user.Units.Count, 0);
            Assert.AreEqual(user.TablePermissions.Count, 0);

            Assert.IsNull(_domain.GetTable(_tableName));
            Assert.IsNull(_domain.GetRole(_roleName));
            Assert.IsNull(_domain.GetUnit(_unitName));
        }


        /// <summary>
        ///   Restoring roles.
        /// </summary>
        [TestMethod]
        public void RestoringRoles()
        {
            BackupAndRestore(DomainComponents.Roles);

            var role = _domain.GetRole(_roleName);

            Assert.IsNotNull(role);

            Assert.AreEqual(role.Users.Count, 0);
            Assert.AreEqual(role.TablePermissions.Count, 0);

            Assert.IsNull(_domain.GetTable(_tableName));
            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetUnit(_unitName));
        }


        /// <summary>
        ///   Restoring users and roles.
        /// </summary>
        [TestMethod]
        public void RestoringUsersAndRoles()
        {
            BackupAndRestore(DomainComponents.Roles
                | DomainComponents.Users);

            var role = _domain.GetRole(_roleName);

            Assert.IsNotNull(role);

            Assert.AreEqual(role.Users.Count, 1);
            Assert.AreEqual(role.Users.Count(u => u.Name == _userName.ToLower()), 1);
            Assert.AreEqual(role.TablePermissions.Count, 0);

            var user = _domain.GetUser(_userName);

            Assert.IsNotNull(user);

            Assert.AreEqual(user.Roles.Count, 1);
            Assert.AreEqual(user.Roles.Count(r => r.Name == _roleName.ToLower()), 1);
            Assert.AreEqual(user.Units.Count, 0);
            Assert.AreEqual(user.TablePermissions.Count, 0);

            Assert.IsNull(_domain.GetTable(_tableName));
            Assert.IsNull(_domain.GetUnit(_unitName));
        }


        /// <summary>
        ///   Restoring units.
        /// </summary>
        [TestMethod]
        public void RestoringUnits()
        {
            BackupAndRestore(DomainComponents.Units);

            var unit = _domain.GetUnit(_unitName);

            Assert.IsNotNull(unit);

            Assert.AreEqual(unit.Users.Count, 0);

            Assert.IsNull(_domain.GetTable(_tableName));
            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetRole(_roleName));
        }


        /// <summary>
        ///   Restoring users and units.
        /// </summary>
        [TestMethod]
        public void RestoringUsersAndUnits()
        {
            BackupAndRestore(DomainComponents.Units
                | DomainComponents.Users);

            var unit = _domain.GetUnit(_unitName);

            Assert.IsNotNull(unit);

            Assert.AreEqual(unit.Users.Count, 1);
            Assert.AreEqual(unit.Users.Count(u => u.Name == _userName.ToLower()), 1);

            var user = _domain.GetUser(_userName);

            Assert.IsNotNull(user);

            Assert.AreEqual(user.Units.Count, 1);
            Assert.AreEqual(user.Units.Count(u => u.Name == _unitName.ToLower()), 1);
            Assert.AreEqual(user.Roles.Count, 0);
            Assert.AreEqual(user.TablePermissions.Count, 0);

            Assert.IsNull(_domain.GetTable(_tableName));
            Assert.IsNull(_domain.GetRole(_roleName));
        }


        /// <summary>
        ///   Restoring table permissions without tables.
        /// </summary>
        [TestMethod]
        public void RestoringPermissionsWithoutTables()
        {
            BackupAndRestore(DomainComponents.TablePermissions
                | DomainComponents.Users
                | DomainComponents.Roles);

            var user = _domain.GetUser(_userName);

            Assert.IsNotNull(user);
            Assert.AreEqual(user.TablePermissions.Count, 0);

            var role = _domain.GetRole(_roleName);

            Assert.IsNotNull(role);
            Assert.AreEqual(role.TablePermissions.Count, 0);

            Assert.IsNull(_domain.GetTable(_tableName));
            Assert.IsNull(_domain.GetUnit(_unitName));
        }


        /// <summary>
        ///   Restoring table permissions without security principals.
        /// </summary>
        [TestMethod]
        public void RestoringPermissionsWithoutPrincipals()
        {
            BackupAndRestore(DomainComponents.TablePermissions
                | DomainComponents.Schema);

            var table = _domain.GetTable(_tableName);

            Assert.IsNotNull(table);
            Assert.IsFalse(table.Permissions.Any(p => p.Principal.Name == _userName.ToLower()));
            Assert.IsFalse(table.Permissions.Any(p => p.Principal.Name == _roleName.ToLower()));

            Assert.IsNull(_domain.GetUnit(_unitName));
            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetRole(_roleName));
        }


        /// <summary>
        ///   Restoring table permissions.
        /// </summary>
        [TestMethod]
        public void RestoringPermissions()
        {
            BackupAndRestore(DomainComponents.TablePermissions
                | DomainComponents.Schema
                | DomainComponents.Users
                | DomainComponents.Roles);

            var table = _domain.GetTable(_tableName);

            Assert.IsNotNull(table);
            Assert.IsTrue(table.Permissions.Any(p => p.Principal.Name == _userName.ToLower()));
            Assert.IsTrue(table.Permissions.Any(p => p.Principal.Name == _roleName.ToLower()));

            var user = _domain.GetUser(_userName);

            Assert.IsNotNull(user);
            Assert.IsTrue(user.TablePermissions.Count > 0);

            var role = _domain.GetRole(_roleName);

            Assert.IsNotNull(role);
            Assert.IsTrue(role.TablePermissions.Count > 0);

            Assert.IsNull(_domain.GetUnit(_unitName));
        }


        /// <summary>
        ///   Restoring rows without tables.
        /// </summary>
        [TestMethod]
        public void RestoringRowsWithoutTables()
        {
            BackupAndRestore(DomainComponents.Rows);

            Assert.IsNull(_domain.GetUnit(_unitName));
            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetRole(_roleName));
            Assert.IsNull(_domain.GetTable(_tableName));
        }


        /// <summary>
        ///   Restoring table rows.
        /// </summary>
        [TestMethod]
        public void RestoringRows()
        {
            BackupAndRestore(DomainComponents.Rows
                | DomainComponents.Schema);

            var table = _domain.GetTable(_tableName);

            Assert.IsNotNull(table);

            Assert.AreEqual(table.InternalRowCount, 1);
            Assert.IsTrue(table.ContainsRow(_rowId, DataSource.InternalTable));
            Assert.IsFalse(table.ContainsRow(_deletedRowId, DataSource.InternalTable));

            Assert.AreEqual(table.SystemColumns.Owner
                .Read(_rowId, DataSource.InternalTable).GetString(), _rowOwner);
            Assert.AreNotEqual(table.SystemColumns.RowVersion
                .Read(_rowId, DataSource.InternalTable).GetGuid(), _rowVersion);

            Assert.IsNull(_domain.GetUnit(_unitName));
            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetRole(_roleName));
        }


        /// <summary>
        ///   Restoring deleted rows.
        /// </summary>
        [TestMethod]
        public void RestoringDeletedRows()
        {
            BackupAndRestore(DomainComponents.Rows
                | DomainComponents.Schema
                | DomainComponents.DeletedRows);

            var table = _domain.GetTable(_tableName);

            Assert.IsNotNull(table);

            Assert.AreEqual(table.InternalRowCount, 2);
            Assert.IsTrue(table.ContainsRow(_rowId, DataSource.InternalTable));
            Assert.IsTrue(table.ContainsRow(_deletedRowId, DataSource.InternalTable));

            Assert.AreEqual(table.SystemColumns.Owner
                .Read(_rowId, DataSource.InternalTable).GetString(), _rowOwner);
            Assert.AreNotEqual(table.SystemColumns.RowVersion
                .Read(_rowId, DataSource.InternalTable).GetGuid(), _rowVersion);

            Assert.IsNull(_domain.GetUnit(_unitName));
            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetRole(_roleName));
        }


        /// <summary>
        ///   Restoring system columns.
        /// </summary>
        [TestMethod]
        public void RestoringSystemColumns()
        {
            BackupAndRestore(DomainComponents.Rows
                | DomainComponents.Schema
                | DomainComponents.SystemColumns);

            var table = _domain.GetTable(_tableName);

            Assert.IsNotNull(table);

            Assert.AreEqual(table.InternalRowCount, 1);
            Assert.IsTrue(table.ContainsRow(_rowId, DataSource.InternalTable));
            Assert.IsFalse(table.ContainsRow(_deletedRowId, DataSource.InternalTable));

            Assert.AreEqual(table.SystemColumns.Owner
                .Read(_rowId, DataSource.InternalTable).GetString(), _rowOwner);
            Assert.AreEqual(table.SystemColumns.RowVersion
                .Read(_rowId, DataSource.InternalTable).GetGuid(), _rowVersion);

            Assert.IsNull(_domain.GetUnit(_unitName));
            Assert.IsNull(_domain.GetUser(_userName));
            Assert.IsNull(_domain.GetRole(_roleName));
        }
    }
}
